import [Scope,Value,Symbol,Number,vList,Function,Errors,math,lString,Character,Macro,LispParser,Stream]
import structs/[ArrayList,HashMap]
import io/[FileReader,File]
// I would implement defun/fset as macros, however i cant because lambda returns a function named "anonymous" and i cant change that from outside the interpreter code

ByPass : class { // Just a random class I use to bypass the bug rock has, will delete this when it is fixed
    call : Func (ArrayList<Value>,Scope,UInt) -> Value
    init : func(=call)
}

BuiltIn : class extends Value {
    name : String
    callback : ByPass
    
    nil : static const Value = vList new()
    t : static const Value = Symbol new("t")
	lisp_stdin : static const Value = Stream new(stdin)
	lisp_stdout : static const Value = Stream new(stdout)
	lisp_stderr : static const Value = Stream new(stderr)
    
    equals? : func(other : Value) -> Bool {
        other class == this class && other as This name == this name
    }
    
    init : func (=name,=callback)
    
    toString : func -> String { name }
    
    fill : static func (scope : Scope) {
        scope["nil"] = nil
        scope["begin"] = This new("begin",ByPass new(begin))
        scope["set"] = This new("set",ByPass new(set))
        scope["if"] = This new("if",ByPass new(_if))
		scope["cond"] = This new("cond",ByPass new(cond))
        scope["="] = This new("=",ByPass new(equals))
        scope[">"] = This new(">",ByPass new(greater))
        scope["<"] = This new("<",ByPass new(lesser))
        scope["+"] = This new("+",ByPass new(plus))
        scope["-"] = This new("-",ByPass new(minus))
        scope["*"] = This new("*",ByPass new(multiply))
        scope["/"] = This new("/",ByPass new(divide))
        scope["mod"] = This new("mod",ByPass new(modulo))
        scope["quote"] = This new("quote",ByPass new(quote))
        scope["while"] = This new("while",ByPass new(_while))
        scope["let"] = This new("let",ByPass new(let))
        scope["defun"] = This new("defun",ByPass new(defun))
        scope["exit"] = This new("exit",ByPass new(exit))
        scope["define"] = This new("define",ByPass new(define))
        scope["let"] = This new("let",ByPass new(let))
        scope["list"] = This new("list",ByPass new(list))
        scope["fset"] = This new("fset",ByPass new(fset))
        scope["and"] = This new("and",ByPass new(and))
        scope["or"] = This new("or",ByPass new(or))
        scope["int"] = This new("int",ByPass new(_int))
        scope["double"] = This new("double",ByPass new(_double))
        scope["load"] = This new("load",ByPass new(load))
        scope["ascii"] = This new("ascii",ByPass new(ascii))
        scope["char"] = This new("char",ByPass new(_char))
        scope["undef"] = This new("undef",ByPass new(undef))
        scope["unset"] = This new("unset",ByPass new(unset))
        scope["nth"] = This new("nth",ByPass new(nth))
        scope["member"] = This new("member",ByPass new(member))
        scope["backquote"] = This new("backquote",ByPass new(backquote))
        scope["comma"] = This new("comma",ByPass new(comma))
        scope["comma-at"] = This new("comma-at",ByPass new(commaAt))
        scope["defmacro"] = This new("defmacro",ByPass new(defmacro))
        scope["lambda"] = This new("lambda",ByPass new(lambda))
        scope["count"] = This new("count",ByPass new(count))
        scope["eval"] = This new("eval",ByPass new(eval))
		// Stream functions ftw
		scope["stream-open"] = This new("stream-open",ByPass new(stream_open))
		scope["stream-new"] = This new("stream-new",ByPass new(stream_new))
		scope["stream-read"] = This new("stream-read",ByPass new(stream_read))
		scope["stream-peek"] = This new("stream-peek",ByPass new(stream_peek))
		scope["stream-print"] = This new("stream-print",ByPass new(stream_print))
		scope["stream-close"] = This new("stream-close",ByPass new(stream_close))

		scope["string?"] = This new("string?",ByPass new(string?))
		scope["char?"] = This new("char?",ByPass new(char?))
		scope["number?"] = This new("number?",ByPass new(number?))
		scope["int?"] = This new("int?",ByPass new(int?))
		scope["double?"] = This new("double?",ByPass new(double?))
		scope["pair?"] = This new("pair?",ByPass new(pair?))
		scope["stream?"] = This new("stream?",ByPass new(stream?))
		scope["symbol?"] = This new("symbol?",ByPass new(symbol?))
		scope["func?"] = This new("func?",ByPass new(funct?))
		scope["macro?"] = This new("macro?",ByPass new(macro?))
		scope["bool?"] = This new("bool?",ByPass new(bool?))

        scope["t"] = t
		scope["stdin"] = lisp_stdin
		scope["stdout"] = lisp_stdout
		scope["stderr"] = lisp_stderr
    }
    
    call : func(arguments : ArrayList<Value>, scope : Scope) -> Value {
        callback call(arguments,scope,line)
    }

	bool? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("bool?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg equals?(t) || arg equals?(nil)) {
			return Symbol new("t",line)
		}

		Symbol new("nil",line)
	}

	macro? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("macro?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg class == Macro) {
			return Symbol new("t",line)
		}

		Symbol new("nil",line)
	}

	funct? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("func?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg class == Function) {
			return Symbol new("t",line)
		}

		Symbol new("nil",line)
	}

	symbol? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("symbol?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg class == Symbol) {
			return Symbol new("t",line)
		}

		Symbol new("nil",line)
	}

	stream? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("stream?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg class == Stream) {
			return Symbol new("t",line)
		}

		Symbol new("nil",line)
	}

	pair? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("pair?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg class == vList) {
			return Symbol new("t",line)
		}

		Symbol new("nil",line)
	}

	double? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("double?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg class == Number) {
			if(!arg as Number int?) {
				return Symbol new("t",line)
			}
		}

		Symbol new("nil",line)
	}

	int? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("int?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg class == Number) {
			if(arg as Number int?) {
				return Symbol new("t",line)
			}
		}

		Symbol new("nil",line)
	}

	number? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("number?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg class == Number) {
			return Symbol new("t",line)
		}

		Symbol new("nil",line)
	}

	char? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("char?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg class == Character) {
			return Symbol new("t",line)
		}

		Symbol new("nil",line)
	}

	string? : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("string?",1,arguments size,line) throw()
		}

		arg := arguments[0] evaluate(scope)
		if(arg class == vList) {
			if(lString new(arg as vList) valid?) {
				return Symbol new("t",line)			
			}		
		}

		Symbol new("nil",line)
	}

	stream_open : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("stream-open",1,arguments size,line) throw()		
		}
		
		arg := arguments[0] evaluate(scope)
		if(arg class != vList) {
			ArgumentTypeExpectedError new("stream-open",1,vList,arg class,arg line) throw()
		}

		str := lString new(arg as vList,arg line)
		if(!str valid?) {
			ArgumentTypeExpectedError new("stream-open",1,lString,vList,str line) throw()
		}

		stream := Stream new(FStream open(str value,"r+"),str line)
		if(stream stream == null) {
			return Symbol new("nil",line)		
		}
		stream
	}

	stream_new : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("stream-new",1,arguments size,line) throw()		
		}
		
		arg := arguments[0] evaluate(scope)
		if(arg class != vList) {
			ArgumentTypeExpectedError new("stream-new",1,vList,arg class,arg line) throw()
		}

		str := lString new(arg as vList,arg line)
		if(!str valid?) {
			ArgumentTypeExpectedError new("stream-new",1,lString,vList,str line) throw()
		}

		stream := Stream new(FStream open(str value,"w+"),str line)
		if(stream stream == null) {
			return Symbol new("nil",line)		
		}
		stream
	}

	stream_close : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("stream-close",1,arguments size,line) throw()		
		}
		
		arg := arguments[0] evaluate(scope)
		if(arg class != Stream) {
			ArgumentTypeExpectedError new("stream-close",1,Stream,arg class,arg line) throw()
		}

		if(arg as Stream stream close() == 0) {
			return Symbol new("t",line)
		}
		Symbol new("nil",line)
	}

	stream_read : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("stream-read",1,arguments size,line) throw()			
		}

		arg := arguments[0] evaluate(scope)
		if(arg class != Stream) {
			ArgumentTypeExpectedError new("stream-read",1,Stream,arg class, arg line) throw()
		}

		if(!arg as Stream stream hasNext?()) {
			return Symbol new("nil",line)		
		}
		Character new(arg as Stream stream readChar(),line) as Value
	}

	stream_peek : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 1) {
			ArgumentsNotEqualToError new("stream-peek",1,arguments size,line) throw()			
		}

		arg := arguments[0] evaluate(scope)
		if(arg class != Stream) {
			ArgumentTypeExpectedError new("stream-peek",1,Stream,arg class, arg line) throw()
		}

		if(!arg as Stream stream hasNext?()) {
			return Symbol new("nil",line)		
		}
		ret := Character new(arg as Stream stream readChar(),line) as Value // Retrieves the character
		arg as Stream stream seek(-1,SEEK_CUR) // Goes back
		ret // Returns the character ;) 
	}

	stream_print : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
		if(arguments size != 2) {
			ArgumentsNotEqualToError new("stream-print",2,arguments size,line) throw()
		}

		arg1 := arguments[0] evaluate(scope)
		if(arg1 class != Stream) {
			ArgumentTypeExpectedError new("stream-print",1,Stream,arg1 class, arg1 line) throw()	
		}

		arg2 := arguments[1] evaluate(scope)
		arg1 as Stream stream write(arg2 toString()) // Writes the data in the stream
		arg2 // And just returns the data
	}
    
    eval : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("eval",1,arguments size,line) throw()
        }
    
        arg := arguments[0] evaluate(scope) evaluate(scope) // Evaluate the argument, as it is done with each function
        arg line = line // ANd then re-evaluate it, as eval is supposed to do that :p 
        arg
    }
    
    begin : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size < 2) {
            ArgumentsLessThanError new("begin",arguments size,2,line) throw()
        }
        for(i in 0 .. arguments size-1) {
            arguments[i] evaluate(scope)
        }
        arguments[arguments size-1] evaluate(scope)
    }
    
    define : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        while(scope parent != null) { // Define writes the variable in the root scope
            scope = scope parent
        }
    
        if(arguments size < 1 || arguments size > 2) {
            ArgumentsLessThanMoreThanError new("define",1,2,arguments size,line) throw()
        }
    
        if(arguments[0] class != Symbol) {
            ArgumentTypeExpectedError new("define",1,Symbol,arguments[0] class,arguments[0] line) throw()
        }
    
        if(arguments size == 2) {
    
            sym := arguments[0] as Symbol name
            value := arguments[1] evaluate(scope)
        
            if(scope contains?(sym)) {
                RedefinitionError new(sym,arguments[0] line) throw()
            }
        
            arguments[0] line = line
            scope[sym] = value
            return arguments[0]
        }
        else {
            sym := arguments[0] as Symbol name
            if(scope contains?(sym)) {
                RedefinitionError new(sym,arguments[0] line) throw()
            }
            scope[sym] = nil
            arguments[0] line = line
            return arguments[0]
        }
        
        null
    }
    
    undef : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
        while(scope parent != null) { // Undef deletes the variable fom the root scope
            scope = scope parent
        }
    
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("undef",1,arguments size,line) throw()
        }
    
        if(arguments[0] class != Symbol) {
            ArgumentTypeExpectedError new("undef",1,Symbol,arguments[0] class,arguments[0] line) throw()
        }
    
        sym := arguments[0] as Symbol name
        if(!scope contains?(sym)) {
            UndefinedError new(sym,arguments[0] line) throw()
        }
    
        old := scope[sym]
        scope remove(sym)
        old line = line
        old
    }
    
    set : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 2) { // Set changes the value of a variable at the local scope
            ArgumentsNotEqualToError new("set",2,arguments size,line) throw()
        }
        
        if(arguments[0] class != Symbol) {
            ArgumentTypeExpectedError new("set",1,Symbol,arguments[0] class,arguments[0] line) throw()
        }
    
        sym := arguments[0] as Symbol name
        value := arguments[1] evaluate(scope)
    
        if(!scope contains?(sym)) {
            UndefinedError new(sym,arguments[0] line) throw()
        }
        old := scope[sym]
        scope[sym] = value
        old line = line
        old
    }
    
    unset : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
        // Unset removes the variable at the local scope
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("unset",1,arguments size,line) throw()
        }
    
        if(arguments[0] class != Symbol) {
            ArgumentTypeExpectedError new("unset",1,Symbol,arguments[0] class,arguments[0] line) throw()
        }
    
        sym := arguments[0] as Symbol name
        if(!scope contains?(sym)) {
            UndefinedError new(sym,arguments[0] line) throw()
        }
    
        old := scope[sym]
        scope remove(sym)
        old line = line
        old
    }
    
    let : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 2) {
            ArgumentsNotEqualToError new("let",2,arguments size,line) throw()
        }
        
        if(arguments[0] class != vList) {
            ArgumentTypeExpectedError new("let",1,vList,arguments[0] class,arguments[0] line) throw()
        }
    
        nScope := Scope new()
        nScope parent = scope
        vars := arguments[0] as vList values
        for(var in vars) {
            if(var class == vList) {
                if(var as vList values size != 2) {
                    ArgumentsNotEqualToError new("let-definition-sublist",2,var as vList values size, var line) throw()
                }
                if(var as vList values[0] class != Symbol) {
                    ArgumentTypeExpectedError new("let-definition-sublist",1,Symbol,var as vList values[0] class, var as vList values[0] line) throw()
                }
                nScope[var as vList values[0] as Symbol name] = var as vList values[1] evaluate(scope)
            }
            else {
                if(vars size != 2) {
                    ArgumentsNotEqualToError new("let-definition-list",2,vars size,arguments[0] line) throw()
                }
                if(var class != Symbol) {
                    ArgumentTypeExpectedError new("let-definition-list",1,Symbol,var class, var line) throw()
                }
        
                nScope[var as Symbol name] = vars[1] evaluate(scope)
                break
            }
        }
        
        arguments[1] evaluate(nScope)
    }
    
    defun : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 3) { // Make sure we have the correct number of arguments
            ArgumentsNotEqualToError new("defun",3,arguments size,line) throw()
        }
    
        if(arguments[0] class != Symbol) { // Make sure first argument is a Symbol
            ArgumentTypeExpectedError new("defun",1,Symbol,arguments[0] class,arguments[0] line) throw()
        }
    
        name := arguments[0] as Symbol name
        if(scope contains?(name)) {
            RedefinitionError new(name,arguments[0] line) throw()// Cannot redefine with defun
        }
    
        args : ArrayList<Symbol> // Ready up the argument list for the function
        if(arguments[1] class == vList && arguments[1] as vList nil?()) { // If we have no arguments (nil)
            args = ArrayList<Symbol> new() // Just make an empty one ;) 
        }
        else if(arguments[1] class == Symbol) {// If we have one argument
            if(arguments[1] as Symbol name != "nil") { // If it is not the nil symbol
                args = ArrayList<Symbol> new() .add(arguments[1] as Symbol) // Just make a list with only it =D
            } else {
                args = ArrayList<Symbol> new() // else its still a nil list
            }
        }
        else if(arguments[1] class == vList) { // Else we MUST have a list
            args = ArrayList<Symbol> new()
            for(val in arguments[1] as vList values) {
                if(val class != Symbol) { // with symbols only
                    ArgumentTypeExpectedError new("defun-arglist",arguments[1] as vList values indexOf(val)+1,Symbol,val class, val line) throw()
                }
                args add(val as Symbol) // so we build the list :)
            }
        }
        else {
            ArgumentTypeExpectedError new("defun",2,vList,arguments[1] class,arguments[1] line) throw()
        }
    
        body := arguments[2] // The body of the function is the third argument, un-evaluated
    
        //Construct the function :)
        function := Function new(name,args,body,line)
        scope[name] = function
        function
    }
    
    lambda : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value { 
        if(arguments size != 2) { // Make sure we have the correct number of arguments
            ArgumentsNotEqualToError new("lambda",2,arguments size,line) throw()
        }
    
        args : ArrayList<Symbol> // Ready up the argument list for the function
        if(arguments[0] class == vList && arguments[0] as vList nil?()) { // If we have no arguments (nil)
            args = ArrayList<Symbol> new() // Just make an empty one ;) 
        }
        else if(arguments[0] class == Symbol) {// If we have one argument
            if(arguments[0] as Symbol name != "nil") { // If it is not the nil symbol
                args = ArrayList<Symbol> new() .add(arguments[0] as Symbol) // Just make a list with only it =D
            } else {
                args = ArrayList<Symbol> new() // else its still a nil list
            }
        }
        else if(arguments[0] class == vList) { // Else we MUST have a list
            args = ArrayList<Symbol> new()
            for(val in arguments[0] as vList values) {
                if(val class != Symbol) { // with symbols only
                    ArgumentTypeExpectedError new("lambda-arglist",arguments[0] as vList values indexOf(val)+1,Symbol,val class, val line) throw()
                }
                args add(val as Symbol) // so we build the list :)
            }
        }
        else {
            ArgumentTypeExpectedError new("lambda",2,vList,arguments[1] class,arguments[0] line) throw()
        }
    
        body := arguments[1] // The body of the function is the third argument, un-evaluated
    
        //Construct the function :)
        Function new("anonymous",args,body,line)
    }
    
    defmacro : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        while(scope parent != null) { // Defmacro defines ont the root scope
            scope = scope parent
        }
    
        if(arguments size != 3) { // Make sure we have the correct number of arguments
            ArgumentsNotEqualToError new("defmacro",3,arguments size,line) throw()
        }
    
        if(arguments[0] class != Symbol) { // Make sure first argument is a Symbol
            ArgumentTypeExpectedError new("defmacro",1,Symbol,arguments[0] class,arguments[0] line) throw()
        }
    
        name := arguments[0] as Symbol name
        if(scope contains?(name)) {
            RedefinitionError new(name,arguments[0] line) throw()// Cannot redefine with defmacro
        }
    
        args : ArrayList<Symbol> // Ready up the argument list for the macro
        if(arguments[1] class == vList && arguments[1] as vList nil?()) { // If we have no arguments (nil)
            args = ArrayList<Symbol> new() // Just make an empty one ;) 
        }
        else if(arguments[1] class == Symbol) {// If we have one argument
            if(arguments[1] as Symbol name != "nil") { // If it is not the nil symbol
                args = ArrayList<Symbol> new() .add(arguments[1] as Symbol) // Just make a list with only it =D
            } else {
                args = ArrayList<Symbol> new() // else its still a nil list
            }
        }
        else if(arguments[1] class == vList) { // Else we MUST have a list
            args = ArrayList<Symbol> new()
            for(val in arguments[1] as vList values) {
                if(val class != Symbol) { // with symbols only
                    ArgumentTypeExpectedError new("defmacro-arglist",arguments[1] as vList values indexOf(val)+1,Symbol,val class, val line) throw()
                }
                args add(val as Symbol) // so we build the list :)
            }
        }
        else {
            ArgumentTypeExpectedError new("defmacro",2,vList,arguments[1] class,arguments[1] line) throw()
        }
    
        body := arguments[2] // The body of the function is the third argument, un-evaluated
    
        //Construct the macro :)
        macro := Macro new(name,args,body,line)
        scope[name] = macro
        macro
    }

    
    fset : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 3) { // Make sure we have the correct number of arguments
            ArgumentsNotEqualToError new("fset",3,arguments size,line) throw()
        }
    
        if(arguments[0] class != Symbol) { // Make sure first argument is a Symbol
            ArgumentTypeExpectedError new("fset",1,Symbol,arguments[0] class,arguments[0] line) throw()
        }
    
        name := arguments[0] as Symbol name
        if(!scope contains?(name)) { // Cannot define with fset
            UndefinedError new(name,arguments[0] line) throw()
        }
    
        args : ArrayList<Symbol> // Ready up the argument list for the function
        if(arguments[1] class == vList && arguments[1] as vList nil?()) { // If we have no arguments (nil)
            args = ArrayList<Symbol> new() // Just make an empty one ;) 
        }
        else if(arguments[1] class == Symbol) { // If we have one argument
            if(arguments[1] as Symbol name != "nil") { // If it is not the nil symbol
                args = ArrayList<Symbol> new() .add(arguments[1] as Symbol) // Just make a list with only it =D
            } else {
                args = ArrayList<Symbol> new() // else its still a nil list
            }
        }
        else if(arguments[1] class == vList) { // Else we MUST have a list
            args = ArrayList<Symbol> new()
            for(val in arguments[1] as vList values) {
                if(val class != Symbol) { // with symbols only
                    //Error
                }
                args add(val as Symbol) // so we build the list :)
            }
        }
        else {
            ArgumentTypeExpectedError new("fset",2,vList,arguments[1] class,arguments[1] line) throw()
        }
    
        body := arguments[2] // The body of the function is the third argument, un-evaluated
    
        //Construct the function :)
        function := Function new(name,args,body,line)
        scope[name] = function
        function
    }

	cond : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size < 1) {
            ArgumentsLessThanError new("cond",arguments size,1,line) throw()
        }
    
        for(arg in arguments) {
			if(arg class != vList) {
				ArgumentTypeExpectedError new("cond",arguments indexOf(arg) + 1, vList,arg class,arg line) throw()
			}
			
			if(arg as vList values size != 2) {
				ArgumentsNotEqualToError new("cond-sublist",2,arg as vList values size,arg line) throw()
			}

			condition := arg as vList values[0] evaluate(scope)
			if(condition != nil) {
				return arg as vList values[1] evaluate(scope)
			}
		}

		Symbol new("nil",line)
    }
    
    _if : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 3) {
            ArgumentsNotEqualToError new("if",3,arguments size,line) throw()
        }
    
        condition := arguments[0] evaluate(scope)
        if(condition != nil) {
            return arguments[1] evaluate(scope)
        }
        arguments[2] evaluate(scope)
    }
    
    equals : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 2) {
            ArgumentsNotEqualToError new("=",2,arguments size,line) throw()
        }
    
        (arguments[0] evaluate(scope) equals?(arguments[1] evaluate(scope))) ? Symbol new("t",line) : vList new(line)
    }
    
    greater : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 2) {
            ArgumentsNotEqualToError new(">",2,arguments size,line) throw()
        }
    
        arguments[0] = arguments[0] evaluate(scope)
        arguments[1] = arguments[1] evaluate(scope)
    
        if(arguments[0] class != Number) {
            ArgumentTypeExpectedError new(">",1,Number,arguments[0] class,arguments[0] line) throw()
        }
        if(arguments[1] class != Number) {
            ArgumentTypeExpectedError new(">",2,Number,arguments[1] class,arguments[1] line) throw()
        }
    
        one : Double = (arguments[0] as Number int?) ? arguments[0] as Number iValue : arguments[0] as Number dValue
        two : Double = (arguments[1] as Number int?) ? arguments[1] as Number iValue : arguments[1] as Number dValue
    
        (one > two) ? Symbol new("t",line) : vList new(line)
    }
    
    lesser : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 2) {
            ArgumentsNotEqualToError new("<",2,arguments size,line) throw()
        }
    
        arguments[0] = arguments[0] evaluate(scope)
        arguments[1] = arguments[1] evaluate(scope)
    
        if(arguments[0] class != Number) {
            ArgumentTypeExpectedError new("<",1,Number,arguments[0] class,arguments[0] line) throw()
        }
        if(arguments[1] class != Number) {
            ArgumentTypeExpectedError new("<",2,Number,arguments[1] class,arguments[1] line) throw()
        }
    
        one : Double = (arguments[0] as Number int?) ? arguments[0] as Number iValue : arguments[0] as Number dValue
        two : Double = (arguments[1] as Number int?) ? arguments[1] as Number iValue : arguments[1] as Number dValue
    
        (one < two) ? Symbol new("t",line) : vList new(line)
    }
    
    and : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        ret? := t
        for(arg in arguments) {
            if(arg evaluate(scope) == nil) {
                ret? = false
            }
        }
        (ret?) ? Symbol new("t",line) : vList new(line)
    }
    
    or : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        ret? := false
        for(arg in arguments) {
            if(arg evaluate(scope) != nil) {
                ret? = t
            }
        }
        (ret?) ? Symbol new("t",line) : vList new(line)
    }
    
    _int : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("int",1,arguments size,line) throw()
        }
    
        arguments[0] = arguments[0] evaluate(scope)
        if(arguments[0] class != Number) {
            ArgumentTypeExpectedError new("int",1,Number,arguments[0] class,arguments[0] line) throw()
        }
    
        Number new(((arguments[0] as Number int?) ? arguments[0] as Number iValue : arguments[0] as Number dValue) as Int,line)
    }
    
    _double : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("Double",1,arguments size,line) throw()
        }
    
        arguments[0] = arguments[0] evaluate(scope)
        if(arguments[0] class != Number) {
            ArgumentTypeExpectedError new("int",1,Number,arguments[0] class,arguments[0] line) throw()
        }
    
        Number new(((arguments[0] as Number int?) ? arguments[0] as Number iValue : arguments[0] as Number dValue) as Double,line)
    }
    
    _char : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("ascii",1,arguments size,line) throw()
        }
    
        arg := arguments[0] evaluate(scope)
        if(arg class != Number) {
            ArgumentTypeExpectedError new("char",1,Number,arguments[0] class,arguments[0] line) throw()
        }
    
        Character new(((arg as Number int?) ? arg as Number iValue : arg as Number dValue as Int) as Char,line) as Value
    }
    
    ascii : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("ascii",1,arguments size,line) throw()
        }
    
        arg := arguments[0] evaluate(scope)
        if(arg class != Character) {
            ArgumentTypeExpectedError new("ascii",1,Character,arguments[0] class,arguments[0] line) throw()
        }
    
        Number new(arg as Character value as Int,line) as Value
    }
    
    plus : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size < 1) {
            ArgumentsLessThanError new("+",arguments size,1,line) throw()
        }
    
        sum : Double = 0
        for(arg in arguments) {
            arg = arg evaluate(scope)
            if(arg class != Number) {
                ArgumentTypeExpectedError new("+",arguments indexOf(arg)+1,Number,arg class,arg line) throw()
            }
            sum += (arg as Number int?) ? arg as Number iValue : arg as Number dValue
        }
    
        num : Number // Initialize the number
        if((sum - (sum as Int)) as Double == 0) { // if numbers after comma are equal to zero
            num = Number new(sum as Int,line) // we have an int :) 
        } else {
            num = Number new(sum,line) // else its a Double :P
        }
        num
    }
    
    minus : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size < 1) {
            ArgumentsLessThanError new("-",arguments size,1,line) throw()
        }
        
        arguments[0] = arguments[0] evaluate(scope)
    
        if(arguments[0] class != Number) {
            ArgumentTypeExpectedError new("-",1,Number,arguments[0] class,arguments[0] line) throw()
        }
    
        if(arguments size == 1) {
            num : Number
            res : Double = -1 * ((arguments[0] as Number int?) ? arguments[0] as Number iValue : arguments[0] as Number dValue) as Double
            if((res - (res as Int)) as Double == 0) { // if numbers after comma are equal to zero
                num = Number new(res as Int,line) // we have an int :) 
            } else {
                num = Number new(res,line) // else its a Double :P
            }
            return num
        }
    
        first : Double = ((arguments[0] as Number int?) ? arguments[0] as Number iValue : arguments[0] as Number dValue) as Double
        for(arg in arguments[1..-1]) {
            arg = arg evaluate(scope)
            if(arg class != Number) {
                ArgumentTypeExpectedError new("-",arguments indexOf(arg)+1,Number,arg class, arg line) throw()
            }
            first -= ((arg as Number int?) ? arg as Number iValue : arg as Number dValue) as Double
        }
    
        num : Number // Initialize the number
        if((first - (first as Int)) as Double == 0) { // if numbers after comma are equal to zero
            num = Number new(first as Int,line) // we have an int :) 
        } else {
            num = Number new(first,line) // else its a Double :P
        }
        num
    }
    
    multiply : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size < 2) {
            ArgumentsLessThanError new("*",arguments size,2,line) throw()
        }
    
        result : Double = 1.0
        for(arg in arguments) {
            arg = arg evaluate(scope)
            if(arg class != Number) {
                ArgumentTypeExpectedError new("*",arguments indexOf(arg)+1,Number,arg class,arg line) throw()
            }
            result = result * ((arg as Number int?) ? arg as Number iValue : arg as Number dValue) as Double
        }
        num : Number // Initialize the number
        if((result - (result as Int)) as Double == 0) { // if numbers after comma are equal to zero
            num = Number new(result as Int,line) // we have an int :) 
        } else {
            num = Number new(result,line) // else its a Double :P
        }
        num
    }
    
    divide : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        
        if(arguments size < 1) {
            ArgumentsLessThanError new("/",arguments size,1,line) throw()
        }
    
    
        result : Double
        arguments[0] = arguments[0] evaluate(scope)
        if(arguments size == 1) {
            if(arguments[0] class != Number) {
                ArgumentTypeExpectedError new("/",1,Number,arguments[0] class,arguments[0] line) throw()
            }
            result = 1/((arguments[0] as Number int?) ? arguments[0] as Number iValue : arguments[0] as Number dValue) as Double
        } else {
            if(arguments[0] class != Number) {
                ArgumentTypeExpectedError new("/",1,Number,arguments[0] class, arguments[0] line) throw()
            }
            result = ((arguments[0] as Number int?) ? arguments[0] as Number iValue : arguments[0] as Number dValue) as Double
            for(arg in arguments[1..-1]) {
                arg = arg evaluate(scope)
                if(arg class != Number) {
                    ArgumentTypeExpectedError new("/",arguments indexOf(arg)+1,Number,arg class, arg line) throw()
                }
                result = result / ((arg as Number int?) ? arg as Number iValue : arg as Number dValue) as Double
            }
        }
    
        num : Number // Initialize the number
        if((result - (result as Int)) as Double == 0) { // if numbers after comma are equal to zero
            num = Number new(result as Int,line) // we have an int :) 
        } else {
            num = Number new(result,line) // else its a Double :P
        }
        num
    }
    
    modulo : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 2) {
            ArgumentsNotEqualToError new("mod",2,arguments size,line) throw()
        }
    
        arguments[0] = arguments[0] evaluate(scope)
        arguments[1] = arguments[1] evaluate(scope)
    
        if(arguments[0] class != Number) {
            ArgumentTypeExpectedError new("mod",1,Number,arguments[0] class,arguments[0] line) throw()
        }
        if(arguments[1] class != Number) {
            ArgumentTypeExpectedError new("mod",2,Number,arguments[1] class,arguments[1] line) throw()
        }
    
        num : Number
        res : Double = ((arguments[0] as Number int?) ? arguments[0] as Number iValue : arguments[0] as Number dValue) as Double mod(((arguments[1] as Number int?) ? arguments[1] as Number iValue : arguments[1] as Number dValue) as Double)
        if((res - (res as Int)) as Double == 0) { // if numbers after comma are equal to zero
            num = Number new(res as Int,line) // we have an int :) 
        } else {
            num = Number new(res,line) // else its a Double :P
        }
        num
    }
    
    quote : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("'",1,arguments size,line) throw()
        }
        
        arguments[0] line = line
        arguments[0]
    }
    
    backquote : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
        // Basically this is backquote,comma and comma-at implementation in one package :) 
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("`",1,arguments size,line) throw()
        }
    
        //Backquote handles all types of arguments
        //If we pass something that is not a list, it just behaves like quote
        if(arguments[0] class != vList) {
            arguments[0] line = line
            return arguments[0]
        }
        
        //If it is a list, however, use the list commaLoop method wich does all the work for us =D
        vList new(arguments[0] as vList commaLoop(scope),line)
    }
    
    comma : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
        // , and ,@ are implemented inside `, so here we just return an out-of-context error
        OutOfContextError new(",","`",line) throw()
    }
    
    commaAt : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
        // , and ,@ are implemented inside `, so here we just return an out-of-context error
        OutOfContextError new(",@","`",line) throw()
    }
    
    _while : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 2) {
            ArgumentsNotEqualToError new("while",2,arguments size,line) throw()
        }
    
        condition := arguments[0] evaluate(scope)
        ret := ArrayList<Value> new()
        while(condition != nil) {
            ret add(arguments[1] evaluate(scope))
            condition = arguments[0] evaluate(scope)
        }
        vList new(ret,line) as Value
    }
    
    list : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        values := ArrayList<Value> new()
        for(arg in arguments) {
            values add(arg evaluate(scope))
        }
        return vList new(values,line) as Value
    }
    
    nth : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
        if(arguments size != 2) {
            ArgumentsNotEqualToError new("nth",2,arguments size,line) throw()
        }
    
        arg1 := arguments[0] evaluate(scope)
        if(arg1 class != Number) {
            ArgumentTypeExpectedError new("nth",1,Number,arg1 class,arg1 line) throw()
        }
        arg2 := arguments[1] evaluate(scope)
        if(arg2 class != vList) {
            ArgumentTypeExpectedError new("nth",2,vList,arg2 class,arg2 line) throw()
        }
    
        n := ((arg1 as Number int?) ? arg1 as Number iValue : arg1 as Number dValue) as Int
        if(n < 0 || n >= arg2 as vList values size) {
            return vList new(line) // Return nil if the element we search is out of bounds
        }
        
        arg2 as vList values[n] // Else return the element :) 
    }
    
    count : static func(arguments : ArrayList<Value>, scope : Scope, line : UInt) -> Value {
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("count",1,arguments size,line) throw()
        }
    
        arg := arguments[0] evaluate(scope)
        if(arg class != vList) {
            ArgumentTypeExpectedError new("count",1,vList,arg class,arg line) throw()
        }
    
        Number new(arg as vList values size as Int,line)
    }
        
    
    member : static func(arguments: ArrayList<Value> , scope : Scope, line : UInt) -> Value {
        if(arguments size != 2) {
            ArgumentsNotEqualToError new("member",2,arguments size,line) throw()
        }
    
        arg1 := arguments[0] evaluate(scope)
        arg2 := arguments[1] evaluate(scope)
        if(arg2 class != vList) {
            ArgumentTypeExpectedError new("member",2,vList,arg2 class,arg2 line) throw()
        }
    
        if(arg2 as vList contains?(arg1)) { // If first argument is contained in the list that is the second one
            return Symbol new("t",line) // Return t :) 
        }
        
        vList new(line) // Else return nil
    }
    
    load : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        if(arguments size != 1) {
            ArgumentsNotEqualToError new("load",1,arguments size,line) throw()
        }
    
        arguments[0] = arguments[0] evaluate(scope)
    
        if(arguments[0] class != vList) {
            ArgumentTypeExpectedError new("load",1,vList,arguments[0] class,arguments[0] line) throw() // Strings are just syntaxic sugar for a lst of characters
        } // So we check if the argument is a list
    
        string := lString new(arguments[0] as vList)
        if(!string valid?) { // And we construct a string from it, wich is valid if it only contains characters
            ArgumentTypeExpectedError new("load",1,lString,vList,string line) throw() 
        }
    
        // load file parse it etc.
        if(File new(string value) exists?()) {
            f := FileReader new(string value)
            data := ""
            while(f hasNext?()) {
                data += f read()
            }
            
            parser := LispParser new(data)
            values := parser work()
            for(val in values) {
                val evaluate(scope)
            }
        
            return Symbol new("t",line)
        }
    
        vList new(line)
    }
    
    exit : static func(arguments : ArrayList<Value>, scope : Scope,line : UInt) -> Value {
        exit(0)
    }
}
